package chromosomes;

import java.text.DecimalFormat;
import java.util.Random;

import logic.Square;
import logic.Sudoku;

/**
 * abstract class Cromosoma
 * 
 * @author Marco Gallardo Casu
 * @author Miguel Cisneros Rojas
 * 
 */
public class Chromosome {

	public final static int LENGTH = 9;

	private int[][] genes;
	private double fitness;
	private double pSelection;
	private double pAccumulated;

	// Publics
	public Chromosome(int[][] genes, final boolean[][] blocked) {
		this.genes = new int[genes.length][];
		for (int square = 0; square < LENGTH; square++) {

			this.genes[square] = genes[square].clone();

			boolean[] mask = new boolean[LENGTH];
			for (int index = 0; index < LENGTH; index++) {
				if (blocked[square][index]) {
					int gen = genes[square][index];
					if (gen != 0) {
						mask[gen - 1] = true;
					}
				}
			}

			for (int index = 0; index < LENGTH; index++) {
				if (!blocked[square][index]) {
					int random = new Random().nextInt(LENGTH);
					while (mask[random]) {
						random = new Random().nextInt(LENGTH);
					}
					this.genes[square][index] = random + 1;
					mask[random] = true;
				}
			}
		}
	}

	private Chromosome(int[][] genes, double fitness, double pSelection,
			double pAccumulated) {
		this.fitness = fitness;
		this.pSelection = pSelection;
		this.pAccumulated = pAccumulated;
		this.genes = new int[genes.length][];
		for (int i = 0; i < this.genes.length; i++) {
			this.genes[i] = genes[i].clone();
		}
	}

	public double evaluate() {
		return fitness = getFitness();
	}

	// Getters and setters.

	public int[][] getGenes() {
		return genes;
	}

	public void setGenes(int[][] genes) {
		this.genes = genes;
	}

	public int getGene(int square, int index) {
		return genes[square][index];
	}

	public void setGene(int square, int index, int gene) {
		genes[square][index] = gene;
	}

	public int[] getSquare(int index) {
		return genes[index];
	}

	public void setSquare(int[] square, int index) {
		genes[index] = square.clone();
	}

	public double getFitness() {
		final int LENGTH = Chromosome.LENGTH;
		final int SQUARE = Square.LENGTH;
		int fitness = LENGTH * LENGTH * 3;
		boolean[] mask = new boolean[LENGTH];
		int gene;
		// For every subsquare
		for (int square = 0; square < LENGTH; square++) {
			mask = new boolean[LENGTH];
			for (int index = 0; index < LENGTH; index++) {
				gene = genes[square][index];
				if (gene > 0) {
					if (mask[gene - 1]) {
						fitness--;
					} else {
						mask[gene - 1] = true;
					}
				}
			}
		}
		// For every row
		int hSquare;
		int hBox;
		for (int row = 0; row < LENGTH; row++) {
			hSquare = row / SQUARE;
			hBox = row % SQUARE;
			mask = new boolean[LENGTH];
			for (int square = 0; square < SQUARE; square++) {
				for (int box = 0; box < SQUARE; box++) {
					gene = genes[hSquare * SQUARE + square][hBox * SQUARE + box];
					if (gene > 0) {
						if (mask[gene - 1]) {
							fitness--;
						} else {
							mask[gene - 1] = true;
						}
					}
				}
			}
		}
		// For every column
		int vSquare;
		int vBox;
		for (int col = 0; col < LENGTH; col++) {
			vSquare = col / SQUARE;
			vBox = col % SQUARE;
			mask = new boolean[LENGTH];
			for (int square = 0; square < SQUARE; square++) {
				for (int box = 0; box < SQUARE; box++) {
					gene = genes[vSquare * SQUARE + square][box * SQUARE + vBox];
					if (gene > 0) {
						if (mask[gene - 1]) {
							fitness--;
						} else {
							mask[gene - 1] = true;
						}
					}
				}
			}
		}
		return fitness;
	}

	public double getpSelection() {
		return pSelection;
	}

	public void setpSelection(double pSelection) {
		this.pSelection = pSelection;
	}

	public double getpAccumulated() {
		return pAccumulated;
	}

	public void setpAccumulated(double pAccumulated) {
		this.pAccumulated = pAccumulated;
	}
	
	public Sudoku getSudoku() {
		return new Sudoku(genes);
	}

	// Aux

	private String fitnessToString() {
		return (new DecimalFormat("#.######")).format(fitness);
	}

	protected String phenotypeToString() {
		String s = "";
		for (int square = 0; square < LENGTH; square++) {
			s += "[ - ";
			for (int box = 0; box < LENGTH; box++) {
				s += genes[square][box] + " - ";
			}
			s += "]\n";
		}
		return s;
	}

	@Override
	public String toString() {
		return "\nFitness: " + fitnessToString() + "\n" + phenotypeToString();
	}

	@Override
	protected Object clone() throws CloneNotSupportedException {
		return new Chromosome(genes, fitness, pSelection, pAccumulated);
	}

}
